package com.heima.gateway.filter;

import com.heima.common.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author: itheima
 * @create: 2021-12-22 11:03
 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    /**
     * 全局过滤器执行逻辑
     *
     * @param exchange 封装请求以及响应对象
     * @param chain    过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.通过参数exchange获取氢气、响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.获取客户端的请求地址 判断是否为登录请求 如果是直接放行
        String url = request.getURI().getPath();
        if (url.contains("/login")) {
            //登录请求直接放行
            return chain.filter(exchange);
        }

        //3.该请求需要进行拦截 获取客户端提交请求头中 token 请求头值
        String token = request.getHeaders().getFirst("token");
        if (StringUtils.isBlank(token)) {
            //该请求需要进行校验令牌，但是没提交，直接返回401Http状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        try {
            //4.通过调用工具类校验JWT令牌合法性（在有效期内，令牌信息没有被篡改）
            //4.1 获取jwt中用户信息
            Claims claims = AppJwtUtil.getClaimsBody(token);
            //4.2 校验JWT令牌是否有效
            int result = AppJwtUtil.verifyToken(claims);
            if (result == -1 || result == 0) {
                //令牌有效
                //5.从JWT载荷中获取到用户ID，用户名称 封装到请求对象中，将参数路由到目标微服务中
                Object userId = claims.get("userId");
                Object userName = claims.get("userName");
                request.mutate().header("userId", userId.toString());
                request.mutate().header("userName", userName.toString());
                return chain.filter(exchange);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        //其他情况统一返回401
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }

    /**
     * 过滤器执行顺序 数值越小优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
